#!/usr/bin/env python
#
# Copyright 2009 Benjamin Davis
#
# This file is part of resistencia.
#
# resistencia is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# resistencia is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with resistencia.  If not, see <http://www.gnu.org/licenses/>.

class Command(str):
    """Command base class"""
    def run(self, player, data):
        pass
    
    
class Help(Command):
    """The 'help' or 'commands' command. Prints a list of commands
    which players can enter.
    
    Format: help"""
    def run(self, player, data):
        player.client.send("Commands:")
        for command in sorted(player.commands):
            player.client.send(command)
    
class Take(Command):
    """The 'take' or 'pick up' commands. Will attempt to take the specified item
    from the room or, optionally, a specified container.
    
    Format: take <item> [from <container>]
    """
    def run(self, player, data):
        # If data contains the word 'from'
        if data.find(' from ') != -1:
            # Split it into item, container
            data, container_name = data.split(' from ')
            # Strip leading and following whitespace from container_name
            container_name = container_name.strip()
            # Set 'container' to the container to take the item from
            container = (player.items + player.room.items).by_name(container_name)
            # If there is no container
            if not container:
                # Give an error message
                player.client.send("There is no", container_name, \
                "to take from.")
                # End
                return
        # If player doesn't specify 'from'
        else:
            # Take the room as the container
            container = player.room
            container_name = 'room'
        # Set 'item' to the item to be picked up
        item = container.items.by_name(data)
        # If the item is in the container/room
        if item:
            # Take the item from the container
            container.items.remove(item)
            
            # If the item is stackable
            try:
                # Check to see if the player already has any of item
                existing_item = player.items.by_name(item.names[0])
                # If they do have some of item
                if existing_item:
                    # Just add items to the pile
                    existing_item.amount += item.amount
                    # Send a confirmation message to the player
                    player.client.send("You take the " + item.name() + ".")
                    for someone in player.room.players:
                        if someone != player:
                            # Broadcast the confirmation message to the room
                            someone.client.send(player.name + " takes the "\
                            + item.name() + ".")
                # If they don't already have any of item
                else:
                    # Act as though item wasn't stackable
                    raise ValueError
            # If the item isn't stackable
            except ValueError:
                # Add to the player's list of items
                player.items.append(item)
                # Send a confirmation message to the player
                player.client.send("You take the " + item.name() + ".")
                for someone in player.room.players:
                    if someone != player:
                        # Broadcast the confirmation message to the room
                        someone.client.send(player.name + " takes the "\
                        + item.name() + ".")
        # If the item isn't in the container
        else:
            # Send an error message
            player.client.send("The", container_name, "doesn't contain a", data + ".")
            
class Drop(Command):
    """The 'drop' or 'put' command. Will attempt to put the specified item into
    the room or, optionally, a specified container.
    
    Format: drop <item> [into <container>]"""
    def run(self, player, data):
        # If data contains the word 'into'
        if data.find(' into ') != -1:
            # Split it into item, container
            data, container_name = data.split(' into ')
            # Strip leading and following whitespace from container_name
            container_name = container_name.strip()
            place = "into the " + container_name
            # Set 'container' to the container to put the item into
            container = (player.items + player.room.items).by_name(container_name)
            # If there is no container
            if not container:
                # Give an error message
                player.client.send("There is no", container_name, \
                "to drop into.")
                # End
                return
        # If player doesn't specify 'into'
        else:
            # Take the room as the container
            container = player.room
            place = 'onto the floor'
        # Set 'item' to the item to be dropped
        item = player.items.by_name(data)
        # If player has the item
        if item:
            # Take the item from the player's inventory
            player.items.remove(item)
            # Put the item into the container/room
            container.items.append(item)
            # Send confirmation message
            player.client.send("You drop the", item.name(), place + ".")
            for someone in player.room.players:
                if someone != player:
                    # Broadcast confirmation message to the room
                    someone.client.send(player.name + " drops the " + item.name() + ".")
        else:
            # Give an error message
            player.client.send("You don't own a " + data + ".")
            
class List(Command):
    """The 'list' or 'inventory' command. Will list the player's inventory.
    
    Format: list"""
    def run(self, player, data):
        items = ''
        # For every item the player owns
        for item in player.items:
            # Add item to the temporary string with a following command
            items += item.name() + ', '
        # Strip the last comma and send output to the player
        player.client.send(items.rstrip(', '))
            
class Look(Command):
    """The 'look' command. Will give the player a description of the room
    along with a list of players and items in the room.
    
    Format: look"""
    def run(self, player, data):
        # Send description of the room to the player
        player.client.send(player.room.description)
        items = ''
        # See List command for the following
        for item in player.room.items:
            items += item.name() + ', '
        player.client.send(items.rstrip(', '))
        
        people = ''
        for someone in player.room.players:
            people += someone.name + ', '
        player.client.send(people.rstrip(', '))
            
class LookAt(Command):
    """The 'look at' command. Will give the player a description of the item.
    
    Format: look at <item>"""
    def run(self, player, data):
        # Look in the player's inventory and the room for the item
        item = (player.items + player.room.items).by_name(data)
        # If found
        if item:
            # Send a formatted description of the item
            player.client.send(item.describe().capitalize() + ".")
        else:
            # Otherwise, send an error message
            player.client.send("You can't see a " + data + ".")
            
class LookIn(Command):
    """The 'look in' command. Will give the player a list of items in the
    specified container.
    
    Format: look in <container>"""
    def run(self, player, data):
        # Look in the player's inventory and the room for the container
        container = (player.items + player.room.items).by_name(data)
        # If found
        if container:
            # Send a list of the items in the container
            items = ''
            for item in container.items:
                items += item.name() + ', '
            player.client.send(items.rstrip(', '))
            
class Say(Command):
    """The 'say' or '.' command. Sends any following text to the room.
    
    Format: say <message>"""
    def run(self, player, data):
        # If player typed a message
        if data != '':
            # Let them know it worked
            player.client.send('You say: "' + data + '"')
            # Broadcast the message to the room
            for someone in player.room.players:
                # But don't send the message to the player that typed it
                if someone != player:
                    someone.client.send(player.name + ' says: "' + data + '"')
                    
class Give(Command):
    """The 'give' command. Will give the specified item to the specified player
    
    Format: give <item> to <player>
    """
    def run(self, player, data):
        # Take the item and receiver names from the data
        _item, _to, _someone = data.partition(' to ')
        # Try to find the item in the giver's inventory
        item = player.items.by_name(_item)
        # Try to find the receiver in the room
        someone = player.room.players.by_name(_someone)
        
        # If the giver doesn't own that item
        if not item:
            player.client.send("You don't own a " + _item)
            
        # If the receiver isn't in the room
        elif not someone:
            player.client.send("There's nobody by the name '" + _someone + "'")
        
        else:
            # Send a message to the giver
            player.client.send("You give a " + item.name() + " to " +\
                someone.name)
            # Send a message to the receiver
            someone.client.send(player.name + " gives you " +\
                item.describe)
            # Remove the item from the giver's inventory
            player.items.remove(item)
            # Put the item into the receiver's inventory
            someone.items.append(item)
    
class Emote(Command):
    """The 'emote' or ':' command. Like 'say' but formatted as:
    <player> <action>
    
    Format: emote <action>"""
    def run(self, player, data):
        for someone in player.room.players:
            someone.client.send(player.name + ' ' + data)
        
class Use(Command):
    """The 'use' command. The same as just calling <item>
    
    Format: use <item>"""
    def run(self, player, data):
        # Act as though player had just typed the item name
        player.do(data)

